package com.nightpiter.view.drawer.rapid;

import java.util.ArrayList;

import com.nightpiter.model.objects.Hub;
import com.nightpiter.model.objects.Station;
import com.nightpiter.model.objects.Transfer;
import com.nightpiter.view.Calculator.Mode;
import com.nightpiter.view.graphics.PointF;

/**
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
abstract class Temp {

	/**
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public static class Knot {

		public static ArrayList<Component> untie(Hub hub, Mode mode) {
			Component[] components = new Component[hub.getStations().size()];
			int i = 0;
			for (Station station : hub.getStations()) {
				components[i++] = new Component(station, mode);
			}

			for (i = 0; i < components.length; i++) {
				Component component = components[i];
				if (!component.isAlive()) {
					continue;
				}
				float minDistance = Float.POSITIVE_INFINITY;
				float distance;
				Component minComponent = null;
				for (int j = i + 1; j < components.length; j++) {
					Component eachComponent = components[j];
					if (!eachComponent.isAlive()) {
						continue;
					}
					if (!component.canTransfer(eachComponent)) {
						continue;
					}
					distance = component.getDistance(eachComponent);
					if (distance < minDistance) {
						minDistance = distance;
						minComponent = eachComponent;
					}
				}
				if (minComponent != null && minDistance < Config.mergeDistance) {
					component.merge(minComponent);
				}
			}

			ArrayList<Component> componentList = new ArrayList<Component>();
			for (Component component : components) {
				if (component.isAlive()) {
					componentList.add(component);
				}
			}
			assert componentList.size() != 0;
			return componentList;

		}

	}

	/**
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public static class Component {

		/** a set of stations represented in this Component instance */
		private ArrayList<Station> stations = new ArrayList<Station>();

		private ArrayList<PointF> coords = new ArrayList<PointF>();

		private PointF coordF = new PointF();

		/** I am not sure that we can remove hashMap entries during an iteration */
		private boolean alive = true;

		private Component(Station station, Mode mode) {
			this.stations.add(station);
			this.coords.add(new PointF(station, mode));
			this.coordF.set(station, mode);
		}

		private boolean canTransfer(Component other) {
			boolean ok = false;
			for (Station station : this.getStations()) {
				for (Station eachStation : other.getStations()) {
					for (Transfer transfer : station.getTransfers()) {
						if (transfer.getToStation() == eachStation) {
							ok = true;
							break;
						}
					}
					if (ok) {
						break;
					}
					for (Transfer transfer : eachStation.getTransfers()) {
						if (transfer.getToStation() == station) {
							ok = true;
							break;
						}
					}
					if (ok) {
						break;
					}
				}
				if (ok) {
					break;
				}
			}
			return ok;
		}

		private float getDistance(Component other) {
			PointF newCoordF = new PointF(coordF);
			PointF otherCoordF = new PointF(other.coordF);
			int thisSize = this.coords.size();
			int otherSize = other.coords.size();
			newCoordF.scale(thisSize);
			otherCoordF.scale(otherSize);
			newCoordF.add(otherCoordF);
			newCoordF.scale(1 / (float) (thisSize + otherSize));

			float maxDistance = Float.NEGATIVE_INFINITY;
			float distance;
			for (PointF eachCoordF : this.coords) {
				distance = newCoordF.distanceTo(eachCoordF);
				if (distance > maxDistance) {
					maxDistance = distance;
				}
			}
			for (PointF eachCoordF : other.coords) {
				distance = newCoordF.distanceTo(eachCoordF);
				if (distance > maxDistance) {
					maxDistance = distance;
				}
			}

			return maxDistance;
		}

		private void merge(Component other) {
			int thisSize = this.coords.size();
			int otherSize = other.coords.size();
			this.coordF.scale(thisSize);
			other.coordF.scale(otherSize);
			this.coordF.add(other.coordF);
			this.coordF.scale(1 / (float) (thisSize + otherSize));

			this.coords.addAll(other.coords);
			this.stations.addAll(other.stations);
			other.alive = false;
		}

		private boolean isAlive() {
			return alive;
		}

		public ArrayList<Station> getStations() {
			return stations;
		}

		public PointF getCoordF() {
			return coordF;
		}

	}

}
